/*
 * $Id: DefaultActionSupport.java 651946 2008-04-27 13:41:38Z apetrelli $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.struts2.dispatcher.ng.filter;

import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.ng.ExecuteOperations;
import org.apache.struts2.dispatcher.ng.InitOperations;
import org.apache.struts2.dispatcher.ng.PrepareOperations;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Handles both the preparation and execution phases of the Struts dispatching process.  This filter is better to use
 * when you don't have another filter that needs access to action context information, such as Sitemesh.
 * 关于servlet的介绍
 * http://blog.csdn.net/tavor/archive/2007/11/09/1876151.aspx
 * filter功能.它使用户可以改变一个 request和修改一个response. Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,
 * 也可以在离开 servlet时处理response.换种说法,filter其实是一个”servlet chaining”(servlet 链).一个filter 包括:
 * 1. 在servlet被调用之前截获;
 * 2. 在servlet被调用之前检查servlet request;
 * 3. 根据需要修改request头和request数据;
 * 4. 根据需要修改response头和response数据;
 * 5. 在servlet被调用之后截获.
 * 一个filter必须实现javax.servlet.Filter接口并定义三个方法:
 * 1.void setFilterConfig(FilterConfig config) //设置filter 的配置对象;
 * 2. FilterConfig getFilterConfig() //返回filter的配置对象;
 * 3. void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) //执行filter 的工作.
 * 若你这个类用到很多常量
 * 可以向这样将这些常量写在这个接口中，而不是定义一个常量类
 * 然后实现这个接口
 * 这里的StrutsStatics接口就是一个例子
 */
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
    protected PrepareOperations prepare;
    protected ExecuteOperations execute;
	protected List<Pattern> excludedPatterns = null;

	/**
	 * 初始化用InitOperations类封装部分初始化动作，该类是struts自己封装的初始化操作类，用来做
	 * 1. 日志初始化
	 * 2. 调度器初始化 
	 *   a, createDispatcher 创建调度器（包括传入 servletContext 和初始化参数map initParam）
	 *   b, 初始化调度器dispatcher并返回改调度器，(本质上是Dispatcher自己的初始化方法)
	 *     包括 创建configurationManager确立defaultFrameworkBeanName="struts"
	 *       init_DefaultProperties 
	 *       init_TraditionalXmlConfigurations	包括取得配置文件名称，并将配置文件的dtd put进去
	 *         用来初始化配置provider。xworks和struts的用if else做区分
	 *         主要是向configurationManager的属性containerProviders（是一个List）中塞东西（ConfigurationProvider继承自ContainerProvider）
	 *      init_LegacyStrutsProperties 类似于b
	 *      init_CustomConfigurationProviders 也类似于b，也不一定真的执行步骤，还是要看initParam中的参数配置
	 *      init_FilterInitParameters 传进去的参数是一个扩展于ConfigurationProvider类的内部类 
	 *      初始化容器，方法为: init_PreloadConfiguration
	 * 3. 根据配置初始化content加载器
	 * 除上述类做的三件事情之外还有
	 * 1. 创建准备操作 prepare 封装的类对应为 PrepareOperations
	 * 2. 创建执行操作 execute 封装的类对应为 ExecuteOperations
	 */
    public void init(FilterConfig filterConfig) throws ServletException {
        InitOperations init = new InitOperations();
        try {
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            init.initLogging(config);
            Dispatcher dispatcher = init.initDispatcher(config);
            init.initStaticContentLoader(config, dispatcher);

            prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
            execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
			this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);

            postInit(dispatcher, filterConfig);
        } finally {
            init.cleanup();
        }

    }

    /**
     * Callback for post initialization
     */
    protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {
            prepare.setEncodingAndLocale(request, response);
            prepare.createActionContext(request, response);
            prepare.assignDispatcherToThread();
			if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
				chain.doFilter(request, response);
			} else {
				request = prepare.wrapRequest(request);
				ActionMapping mapping = prepare.findActionMapping(request, response, true);
				if (mapping == null) {
					boolean handled = execute.executeStaticResourceRequest(request, response);
					if (!handled) {
						chain.doFilter(request, response);
					}
				} else {
					execute.executeAction(request, response, mapping);
				}
			}
        } finally {
            prepare.cleanupRequest(request);
        }
    }

    public void destroy() {
        prepare.cleanupDispatcher();
    }
}
